#include "mytablemodel.h"
#include <eigenhelper.h>
#include <rapidcsv.h>

MyTableModel::MyTableModel(QObject *parent) : QAbstractTableModel(parent), m_x(), m_y() {
    m_header << "X" << "Y";
    setMinMax();
}

int MyTableModel::rowCount(const QModelIndex &parent) const {
    Q_UNUSED(parent)
    return m_x.size();
}

int MyTableModel::columnCount(const QModelIndex &parent) const {
    Q_UNUSED(parent)
    return 2;
}

bool MyTableModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) {
    if (orientation != Qt::Horizontal) {
        return false;
    }
    if (role != Qt::DisplayRole && role != Qt::EditRole) {
        return false;
    }
    auto str = value.toString();
    if (str.isEmpty()) {
        return false;
    }
    m_header.replace(section % 2, str);
    emit headerDataChanged(Qt::Horizontal, section % 2, section % 2);
    return true;
}

QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (role != Qt::DisplayRole && role != Qt::EditRole)
        return QVariant();

    if (orientation == Qt::Horizontal) {
        return m_header.at(section % 2);
    } else {
        return QString("%1").arg(section + 1);
    }
}

QVariant MyTableModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid()) {
        return QVariant();
    }
    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);

    } else if ((role == Qt::DisplayRole || role == Qt::EditRole) && index.row() < m_x.size() && index.column() < 2) {
        switch (index.column()) {
            case 0:
                return m_x.at(index.row());
            case 1:
                return m_y.at(index.row());
        }
    }
    return QVariant();
}

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
    if (index.isValid() && role == Qt::EditRole && index.row() < m_x.size() && index.column() < 2) {
        auto val = value.toDouble();
        switch (index.column()) {
            case 0:
                m_x.replace(index.row(), val);
                if (val < m_xMin) {
                    m_xMin = val;
                }
                if (val > m_xMax) {
                    m_xMax = val;
                }
                break;
            case 1:
                if (val < m_yMin) {
                    m_yMin = val;
                }
                if (val > m_yMax) {
                    m_yMax = val;
                }
                m_y.replace(index.row(), val);
                break;
        }
        emit dataChanged(index, index);
        return true;
    }
    return false;
}

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const {
    return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

bool MyTableModel::setFromEigen(const Eigen::VectorXd &x, const Eigen::VectorXd &y) {
    return setFromQVector(
            QVector<double>(x.data(), x.data() + x.size()),
            QVector<double>(x.data(), x.data() + x.size()));
}

bool MyTableModel::setFromStd(const std::vector<double> &x, const std::vector<double> &y) {
    return setFromQVector(
            QVector<double>(x.begin(), x.end()),
            QVector<double>(y.begin(), y.end()));
}

bool MyTableModel::setFromQVector(const QVector<double> &x, const QVector<double> &y) {
    if (x.size() != y.size()) {
        return false;
    }
    beginResetModel();
    m_x = x;
    m_y = y;
    setMinMax();
    endResetModel();
    return true;
}


bool MyTableModel::setFromCsv(const QString &path, size_t x_col, size_t y_col) {
    try {
        rapidcsv::Document doc(path.toLocal8Bit().toStdString());
        auto x = doc.GetColumn<double>(x_col);
        auto y = doc.GetColumn<double>(y_col);

        auto ret = setFromStd(x, y);
        if (ret) {
            setHeaderData(0, Qt::Orientation::Horizontal,
                          QString::fromLocal8Bit(doc.GetColumnName(x_col).c_str()));
            setHeaderData(1, Qt::Orientation::Horizontal,
                          QString::fromLocal8Bit(doc.GetColumnName(y_col).c_str()));
            return true;
        } else {
            return false;
        }
    } catch (const std::exception &e) {
        std::cout << e.what() << std::endl;
        return false;
    }
}


double MyTableModel::xMin() const {
    return m_xMin;
}

double MyTableModel::yMin() const {
    return m_yMin;
}

double MyTableModel::xMax() const {
    return m_xMax;
}

double MyTableModel::yMax() const {
    return m_yMax;
}


QVector<double> &MyTableModel::getX() {
    return m_x;
}

QVector<double> &MyTableModel::getY() {
    return m_y;
}


void MyTableModel::setMinMax() {
    if (m_x.isEmpty()) {
        m_xMin = 0;
        m_xMax = 1.0;
    } else {
        auto ans = std::minmax_element(m_x.begin(), m_x.end());
        m_xMin = *ans.first;
        m_xMax = *ans.second;
    }
    if (m_y.isEmpty()) {
        m_yMin = 0;
        m_yMax = 1.0;
    } else {
        auto ans = std::minmax_element(m_y.begin(), m_y.end());
        m_yMin = *ans.first;
        m_yMax = *ans.second;
    }
}

